{- |

Module      :  Examples.LLDirect
Copyright   :  (c) The University of Kansas 2011
License     :  BSD3

Maintainer  :  nicolas.frisby@gmail.com
Stability   :  experimental
Portability :  see LANGUAGE pragmas (... GHC)

Lambda-lifts 'Examples.InnerBase.Inner' to 'Examples.LLBase.LL' without using
@yoko@. Compare to "Examples.LL0" and "Examples.LL".

-}
module Examples.LLDirect where

import Examples.TermBase (Type(..))
import Examples.InnerBase (Inner)
import qualified Examples.InnerBase as I
import Examples.LLBase

import qualified Data.Set as Set; import Data.Set (Set)
import qualified Data.IntMap as IM; import Data.IntMap (IntMap)



fvs :: Inner -> Set Int
fvs (I.Lam ty tm) = Set.map (subtract 1) $ Set.filter (> 0) $ fvs tm
fvs (I.Var i) = Set.singleton i
fvs (I.App tm1 tm2) = fvs tm1 `Set.union` fvs tm2

bump :: Set Int -> Set Int
bump = Set.map (subtract 1) . Set.filter (> 0)

renm :: IntMap Int -> Int -> Term -> Term
renm m dv tm@(Var i) = maybe tm Var $ IM.lookup i m
renm m dv (App tm1 tm2) = App (renm m dv tm1) (renm m dv tm2)
renm _ dv (DVar i) = DVar (i + dv)

renmP :: IntMap Int -> Int -> Prog -> Prog
renmP m dv (Prog tlds main) = Prog (map each tlds) (renm m dv main) where
  each (tys, ty, tm) = (tys, ty, renm m dv tm)




type Env = [Type]

lambdaLift :: Inner -> Env -> Prog
lambdaLift (I.Lam ty tm) rho = newTLD ty rho (fvs tm) $ lambdaLift tm (ty : rho)
lambdaLift (I.Var i) rho = Prog [] $ Var i
lambdaLift (I.App tm1 tm2) rho = Prog (tlds1 ++ tlds2) $ App main1 main2 where
  Prog tlds1 main1 = lambdaLift tm1 rho
  Prog tlds2 main2 = renmP IM.empty (length tlds1) $ lambdaLift tm2 rho



newTLD :: Type -> Env -> Set Int -> Prog -> Prog
-- NB could check if such a TLD already exists; lambdaLift@I.App would need to handle
-- that too
newTLD ty rho fvs (Prog tlds main) =
  Prog ((map (rho !!) fvs', ty, renm rn 0 main) : tlds) $
  foldl ((. Var) . App) (DVar 0) fvs'
  where rn = IM.fromDistinctAscList $ zip (Set.toAscList fvs) [0..]
        fvs' = reverse $ Set.toAscList $ bump fvs





env0 = [TBool, TBool, TArrow TInt TInt, TInt]
ex0 = I.Lam TInt $ I.Lam TInt $ I.Var 4 `I.App` I.Var 1 `I.App` I.Var 0
ex1 = ex0 `I.App` I.Var 3
ex2 = (I.Lam (TArrow TInt TInt `TArrow` TArrow TInt TInt) $ I.Var 0) `I.App`
      (I.Lam (TArrow TInt TInt) $ I.Var 0)


-- *LL> ll ex1 env0
-- Prog [([TArrow TInt TInt],TInt,App (App (DVar 0) (Var 1)) (Var 0)),
--       ([TArrow TInt TInt,TInt],TInt,App (App (Var 2) (Var 1)) (Var 0))
--      ] (App (App (DVar 0) (Var 2)) (Var 3))
